package org.meb.speedway.bean;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.annotation.Resource;
import javax.ejb.EJBContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.meb.commons.DateUtils;
import org.meb.commons.Utils;
import org.meb.intercept.Loggable;
import org.meb.speedway.config.SpeedwayDatabase;
import org.meb.speedway.dao.BaseDao;
import org.meb.speedway.dao.DaoResolver;
import org.meb.speedway.model.common.Config;
import org.meb.speedway.model.common.IntEvent;
import org.meb.speedway.model.registry.League;
import org.meb.speedway.model.registry.LeagueTeamHistory;
import org.meb.speedway.model.registry.Team;
import org.meb.speedway.query.LeagueQuery;
import org.meb.speedway.query.TeamQuery;
import org.meb.speedway.util.Patterns;

@Stateless
public class ImportServiceBean implements ImportService {

	@Inject
	@SpeedwayDatabase
	private EntityManager em;

	@Inject
	private DaoResolver daoResolver;

	@Resource
	private EJBContext context;

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public String importLeagues(String data) throws IOException {
		StringBuilder log = new StringBuilder();

		String[][] tokensArray = tokenize(data, ";");
		String leagueCode;
		String leagueName;
		try {
			BaseDao<League> dao = daoResolver.resolve(League.class);
			for (String[] tokens : tokensArray) {
				leagueCode = tokens[0];
				leagueName = tokens[1];
				League league = dao.find(League.class, leagueCode);
				if (league == null) {
					league = new League();
				}
				league.setCode(leagueCode);
				league.setName(leagueName);
				if (league.getVersion() == null) {
					log.append("persist: " + league);
					log.append("\n");
					dao.persist(league);
				} else {
					log.append("merge: " + league);
					log.append("\n");
					dao.merge(league);
				}
			}
			dao.getEntityManager().flush();
		} catch (RuntimeException e) {
			System.out.println(log.toString());
			throw e;
		}
		return log.toString();
	}

	@Override
	public String importTeams(String data) throws IOException {
		// teamDao.setEntityManager(em);
		StringBuilder log = new StringBuilder();
		String[][] tokensArray = tokenize(data, ";");
		String leagueCode;
		Integer year;
		String teamName;
		try {
			for (String[] tokens : tokensArray) {
				leagueCode = tokens[0];
				year = Integer.valueOf(tokens[1]);
				teamName = tokens[2];

				BaseDao<Team> teamDao = daoResolver.resolve(Team.class);
				TeamQuery teamQuery = new TeamQuery();
				teamQuery.getEntity().setName(teamName);
				Team team = teamDao.findUnique(teamQuery);
				if (team == null) {
					team = new Team(teamName);
				}
				if (team.getVersion() == null) {
					log.append("persist: " + team);
					log.append("\n");
					teamDao.persist(team);
				} else {
					log.append("merge: " + team);
					log.append("\n");
					team = teamDao.merge(team);
				}

				BaseDao<League> leagueDao = daoResolver.resolve(League.class);
				LeagueQuery leagueQuery = new LeagueQuery();
				leagueQuery.getEntity().setName(leagueCode);
				League league = leagueDao.findUnique(leagueQuery);
				if (league.getTeamsHistory() == null) {
					league.setTeamsHistory(new ArrayList<LeagueTeamHistory>());
				}
				LeagueTeamHistory ltHistory = new LeagueTeamHistory();
				ltHistory.setLeague(league);
				ltHistory.setTeam(team);
				ltHistory.setYear(year);
				if (!league.getTeamsHistory().contains(ltHistory)) {
					league.getTeamsHistory().add(ltHistory);
				}
				if (league.getVersion() == null) {
					log.append("persist: " + league);
					log.append("\n");
					leagueDao.persist(league);
				} else {
					log.append("merge: " + league);
					log.append("\n");
					league = leagueDao.merge(league);
				}
				// em.flush();
			}
		} catch (RuntimeException e) {
			System.out.println("exception:" + log.toString());
			throw e;
		}
		return log.toString();
	}

	@Override
	public String importRiders(String data) throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	private String[][] tokenize(String data, String delim) throws IOException {
		ArrayList<String[]> list = new ArrayList<String[]>();
		BufferedReader reader = new BufferedReader(new StringReader(data));
		String line;
		while ((line = reader.readLine()) != null) {
			list.add(line.split(delim));
		}
		return list.toArray(new String[list.size()][]);
	}

	@Override
	@Loggable
	public String importEventSources() {
		SimpleDateFormat eventDateFmt = new SimpleDateFormat(Patterns.DATE_LONG);
		SimpleDateFormat eventDateFmtR = new SimpleDateFormat(Patterns.DATE_LONG_R);
		SimpleDateFormat updateDateFmt = new SimpleDateFormat(Patterns.TIMESTAMP_R);
		SimpleDateFormat filterFmt = new SimpleDateFormat("yyyyMMddHHmm");
		Config markDateConfig = em.find(Config.class, "evtsrc.read.date");
		Date markDate = markDateConfig.getDate();
		Date maxUpdateDate = markDate;

		StringBuilder log = new StringBuilder();
		String url = "http://spdwldr00.appspot.com/event/list?f=" + filterFmt.format(markDate);
		Document doc = null;
		try {
			doc = Jsoup.connect(url).get();
		} catch (IOException e) {
			log.append("[ERROR]: unable to connect to: ").append(url).append(": ")
					.append(e.getMessage()).append("\n");
			return log.toString();
		}

		int persisted = 0;
		int merged = 0;
		int skipped = 0;
		int errors = 0;
		BaseDao<IntEvent> dao = daoResolver.resolve(IntEvent.class);
		Elements trs = doc.select("tbody tr");
		for (int i = 0; i < 1; i++) {
			Element tr = trs.get(i);
			Elements tds = tr.select("td");

			String eventUri = tds.get(6).text();
			if (StringUtils.isNotBlank(eventUri)) {
				eventUri = eventUri.trim();
			} else {
				log.append("[ERROR]: event uri is blank").append("\n");
				errors++;
				continue;
			}

			// create new task to be persisted/merged
			Date operDate = new Date();
			IntEvent intEvent = new IntEvent();
			intEvent.setStatus(IntEvent.Status.REGISTERED);
			intEvent.setEventUri(eventUri);
			intEvent.getFingerprint().setInsertDate(operDate);
			intEvent.getFingerprint().setUpdateDate(operDate);
			intEvent.setEventGroup(tds.get(2).text());
			intEvent.setEventName(tds.get(3).text());
			String dateString = tds.get(4).text() + " " + tds.get(5).text();
			try {
				intEvent.setEventDate(eventDateFmt.parse(dateString));
			} catch (ParseException e) {
				log.append("[ERROR]: unable to parse date string: ").append(dateString)
						.append(": ").append(e.getMessage()).append("\n");
				errors++;
				continue;
			}

			String updateDateString = tds.get(1).text();
			try {
				Date updateDate = updateDateFmt.parse(updateDateString);
				if (updateDate.compareTo(maxUpdateDate) > 0) {
					maxUpdateDate = updateDate;
				}
			} catch (ParseException e) {
				log.append("[WARN]: unable to parse update date string: ").append(dateString)
						.append(": ").append(e.getMessage()).append("\n");
			}

			// find persistent task with matching event uri
			EventTaskQuery query = new EventTaskQuery();
			query.getEntity().setEventUri(eventUri);
			IntEvent persistent = dao.findUnique(query);
			StringBuilder detailLog = null;
			try {
				boolean changed = false;
				if (persistent == null) {
					dao.persist(intEvent);
					log.append("[INFO]: persisted: ");
					changed = true;
					persisted++;
				} else {
					CompareToBuilder cmp = new CompareToBuilder();
					cmp.append(intEvent.getEventDate(), persistent.getEventDate());
					cmp.append(intEvent.getEventGroup(), persistent.getEventGroup());
					cmp.append(intEvent.getEventName(), persistent.getEventName());
					if (cmp.toComparison() != 0) {
						Date eventDate = intEvent.getEventDate();
						String eventName = intEvent.getEventName();
						String eventGroup = intEvent.getEventGroup();
						detailLog = new StringBuilder();
						Utils.append(detailLog, "|",
								DateUtils.format(persistent.getEventDate(), eventDateFmtR, "null"),
								" --> ", DateUtils.format(eventDate, eventDateFmtR, "null"));
						Utils.append(detailLog, "|", persistent.getEventGroup(), " --> ",
								eventGroup);
						Utils.append(detailLog, "|", persistent.getEventName(), " --> ", eventName);

						persistent.setEventDate(eventDate);
						persistent.setEventGroup(eventGroup);
						persistent.setEventName(eventName);
						intEvent = dao.merge(persistent);
						log.append("[INFO]: merged: ");
						changed = true;
						merged++;
					} else {
						skipped++;
					}
				}
				if (changed) {
					log.append(eventDateFmtR.format(intEvent.getEventDate())).append(", ")
							.append(intEvent.getEventGroup()).append(", ")
							.append(intEvent.getEventName())
							.append(detailLog == null ? "" : detailLog.toString()).append("\n");
				}
			} catch (Exception e) {
				detailLog = null;
				log.append("[ERROR]: unable to persist/merge").append("\n");
				errors++;
				continue;
			}

		}
		log.append(String.format("Processed: persisted: %d, merged: %d, skipped: %d, errors: %d\n",
				persisted, merged, skipped, errors));
		markDateConfig.setDate(maxUpdateDate);
		markDateConfig = em.merge(markDateConfig);
		log.append("Updated ").append(markDateConfig.getCode()).append(" to ")
				.append(updateDateFmt.format(markDateConfig.getDate()));
		em.flush();
		throw new RuntimeException("ERROR");

		// return log.toString();
	}
}
